<?PHP
/**
 * OpenStation
 *
 *
 * @Application : OpenStation.php
 * @Encode : 65001 Unicode ( UTF-8 NO BOM )
 * @Description : OpenStation Core
 * @Author : HK <hkyyang@gmail.com>
 */

if( ! defined( 'APP_PATH' ) || ! defined( 'APP_FILE' ) || ! defined( 'OPENSTATION_MODE' ) ) {
    exit;
}

if( ( float ) PHP_VERSION < 5.0 ) {
    exit( 'Error: Need PHP version 5.0 or greater' );
}

if( ! in_array( strtoupper( OPENSTATION_MODE ), array( 'NORMAL', 'CLI' ) ) ) {
    exit( 'ERROR: OPENSTATION_MODE "' . OPENSTATION_MODE . '"' );
}

define( 'OS_VER', '1.6.5' );
define( 'DS', DIRECTORY_SEPARATOR );
define( 'PHP_VER', ( float ) PHP_VERSION );
define( 'OS_PATH', dirname( __FILE__ ) . DS );
define( 'MEMORY_COUNT_ON', function_exists( 'memory_get_usage' ) );
define( 'IS_CGI', substr( PHP_SAPI, 0, 3 ) == 'cgi' ? TRUE : FALSE );
define( 'IS_WIN', DS == '/' ? FALSE : TRUE );
define( 'LINE_BREAK', ! IS_WIN ? "\r\n" : "\n" );
define( 'TABS', "\t" );
define( 'IS_CLI', PHP_SAPI == 'cli' ? TRUE : FALSE );
define( 'MAGIC_QUOTES_GPC', get_magic_quotes_gpc() );

define( 'ON', 1 );
define( 'OFF', 0 );

// URL Mode
define( 'URL_NORMAL', 0 );
define( 'URL_PATHINFO', 1 );
define( 'URL_REWRITE', 2 );
define( 'URL_AUTO', 3 );

memory_count( 'OPENSTATION' );
runtime_count( 'OPENSTATION' );

$APP_PATH = str_replace( '\\', '/', APP_PATH );

$Paths = array(
    'CONFIG' => $APP_PATH . 'Config/',
    'DATA' => $APP_PATH . 'Data/',
    'CACHE' => $APP_PATH . 'Data/Cache/',
    'LOG' => $APP_PATH . 'Data/Log/',
    'TEMP' => $APP_PATH . 'Data/Temp/',
    'BUILD' => $APP_PATH . 'Data/Build/',
    'COMPILE' => $APP_PATH . 'Data/Compile/',
    'SESSION' => $APP_PATH . 'Data/Session/',
    'TEMPLATE' => $APP_PATH . 'Template/',
    'INCLUDE' => $APP_PATH . 'Include/',
    'LIB' => $APP_PATH . 'Lib/',
    'ACTION' => $APP_PATH . 'Lib/Action/',
    'MODEL' => $APP_PATH . 'Lib/Model/',
    'CLI' => $APP_PATH . 'Lib/Cli/',
    'STATIC' => $APP_PATH . 'Static/',
    'STYLE' => $APP_PATH . 'Static/Styles/',
    'SCRIPT' => $APP_PATH . 'Static/Scripts/',
    'IMAGE' => $APP_PATH . 'Static/Images/',
);

unset( $APP_PATH );

// Only Cli
if( strtoupper( OPENSTATION_MODE ) == 'CLI' ) {
    require OS_PATH . 'Extends/OpenCli.php';
// Build Core
} else if( is_file( APP_PATH . 'Data/Build/CORE-' . strtoupper( PHP_SAPI ) . '.cache.php' ) ) {
    import( APP_PATH . 'Data/Build/CORE-' . strtoupper( PHP_SAPI ) . '.cache.php', NULL );
} else {
    // App initial
    if( ! is_file( APP_PATH . 'Config/Config.php' ) ) {
        array_walk( $Paths, 'mkdirs' );
        copy( OS_PATH . 'Template/InitialDefault/APP_CONFIG', APP_PATH . 'Config/Config.php' );
    }
    build_core_cache( APP_PATH . 'Data/Build/CORE-' . strtoupper( PHP_SAPI ) . '.cache.php', TRUE );
    import( APP_PATH . 'Data/Build/CORE-' . strtoupper( PHP_SAPI ) . '.cache.php', NULL );
}
$short_name = get_file_name( APP_FILE );
$action_file = APP_PATH . 'Lib/Action/' . ucfirst( strtolower( $short_name ) ) . 'Action.class.php';
// Action initial
if( ! is_file( $action_file ) ) {
    copy( OS_PATH . 'Template/InitialDefault/APP_ACTION', $action_file );
    file_put_contents( $action_file, str_replace(
        array( '%CLASS%', '%METHOD%' ),
        array(  ucfirst( strtolower( $short_name ) ) . 'Action', 'index' ),
        file_get_contents( $action_file )
    ) );
}

Cache :: set( 'SERVER', array(
    'CACHE_FILE' => APP_PATH . 'Data/Build/CORE-' . strtoupper( PHP_SAPI ) . '.cache.php',
    'ACTION_SHORT' => ucfirst( strtolower( string_after_remove( basename( APP_FILE ), '.php' ) ) ),
    'ENTRANCE' => basename( APP_FILE ),
    'ACTION_FILE_NAME' => $action_file,
) );

Cache :: set( 'APP_PATH', $Paths );
Cache :: load( 'OPENSTATION', OS_PATH . 'Config/Config.php' );
Cache :: load( '@', APP_PATH . 'Config/Config.php' );
Cache :: lang( include OS_PATH . 'Lang/' . Cache :: get( 'OPENSTATION.OTHER.LANGUAGE' ) . '.php' );

if( function_exists( 'gzdeflate' ) && function_exists( 'gzinflate' ) ) {
    Cache :: set( 'OPENSTATION.COMPRESSION', array(
        'ENCODE' => 'gzdeflate',
        'DECODE' => 'gzinflate'
    ) );
} else if( function_exists( 'gzcompress' ) && function_exists( 'gzuncompress' ) ) {
    Cache :: set( 'OPENSTATION.COMPRESSION', array(
        'ENCODE' => 'gzcompress',
        'DECODE' => 'gzuncompress'
    ) );
}

if( function_exists( 'date_default_timezone_set') ) {
    date_default_timezone_set( auto_get_config( 'OTHER.TIME_ZONE' ) );
}
unset( $Paths );
App :: run();

// ---------------------------------------------------------------------------- Common Functions

/**
 * @param String : $name
 * @param String : $ext
 * @param String : $callback_ext
 * @return Array
 */
function import( $name = NULL, $ext = '.class.php', $callback_ext = NULL ) {

    static $_import = array();

    if( empty( $name ) ) {
        return $_import;
    } else if( is_array( $name ) ) {
        array_walk( $name, 'import', $callback_ext );
        return $_import;
    }

    if( ! is_file( $name ) ) {
        if( $name{0} != '@' ) {
            $name = OS_PATH . $name;
        }
        if( ! is_file( $name ) ) {
            $name = str_replace( array( '@', '#', '.' ), array( APP_PATH, '.', DS ), $name );
        }
    }

    if( ! empty( $callback_ext ) ) {
        $ext = $callback_ext;
    }

    // PACKAGE
    if( $name{strlen( $name ) - 1} == '*' && is_dir( dirname( $name ) ) ) {
        array_walk( glob( $name . $ext ), 'import', $ext );
    // CLASS
    } else {
        $name = string_append_replace( $name, $ext );
        if( is_file( $name ) && ! in_array( $name, $_import ) ) {
            include_once( $name );
            $_import[] = path_replace( $name );
        }
    }
    return $_import;
}

/**
 * 路径规范统一
 *
 * @param String : $path
 * @return String
 */
function path_replace( $path, $DS = NULL ) {
    $DS = empty( $DS ) ? DS : $DS;
    return preg_replace( array( '/\\\\\/+/', '/\/\\\\+/', '/\\\\+|\/+/' ), $DS, $path );
}

/**
 * 递归创建目录
 *
 * @param String : $path
 * @param Integer : $mode
 * @return Boolean
 */
function mkdirs( $path, $mode = 0777 ) {
    if( ! is_int( $mode ) ) {
        $mode = 0777;
    }
    if( is_dir( $path ) ) {
        return TRUE;
    } else if( is_dir( dirname( $path ) ) ) {
        return @mkdir( $path, $mode );
    } else if( ! mkdirs( dirname( $path ), $mode ) ) {
        return FALSE;
    }
    return mkdir( $path, $mode );
}

/**
 * 递归删除文件或目录
 *
 * @param String | Array : $dir
 * @param Boolean : $struct, 保留目录结构
 * @return Boolean
 */
function rm( $dir, $struct = FALSE, $structure = NULL ) {
    $struct = is_null( $structure ) ? $struct : $structure;
    if( is_array( $dir ) ) {
        return array_walk( $dir, 'rm', $struct );
    } else if( is_file( $dir ) ) {
        return @ unlink( $dir );
    } else if( is_dir( $dir ) ) {
        // 递归删除子目录及文件
        array_walk( @ glob( string_append_replace( $dir, DS ) . '*', GLOB_MARK ), 'rm', $struct );
    }
    if( FALSE === $struct && is_dir( $dir ) ) {
        return @ rmdir( $dir );
    }
    return TRUE;
}

/**
 * 替换追加字符串到末尾
 *
 * @param String : $content
 * @param String : $str
 * @return String
 */
function string_append_replace( $content, $str ) {
    if( substr( $content, - strlen( $str ) ) == $str ) {
        return $content;
    }
    return $content . $str;
}

/**
 * 替换追加字符串到前端
 *
 * @param String : $content
 * @param String : $str
 * @return String
 */
function string_prepend_replace( $content, $str ) {
    if( substr( $content, 0, strlen( $str ) ) == $str ) {
        return $content;
    }
    return $str . $content;
}

/**
 * 删除字符串末尾的指定字符串，如果有的话
 *
 * @param String : $content
 * @param String | Array : $str
 * @return String
 */
function string_after_remove( $content, $str ) {
    if( is_array( $str ) ) {
        foreach( $str as $v ) {
            if( substr( $content, - strlen( $v ) ) == $v ) {
                return substr( $content, 0, - strlen( $v ) );
            }
        }
    } else if( substr( $content, - strlen( $str ) ) == $str ) {
        return substr( $content, 0, - strlen( $str ) );
    }
    return $content;
}

/**
 * 删除字符串前端的指定字符串，如果有的话
 *
 * @param String : $content
 * @param String | Array : $str
 * @return String
 */
function string_before_remove( $content, $str ) {
    if( is_array( $str ) ) {
        foreach( $str as $v ) {
            if( strpos( $content, $v ) === 0 ) {
                return substr( $content, strlen( $str ) );
            }
        }
    } else if( strpos( $content, $str ) === 0 ) {
        return substr( $content, strlen( $str ) );
    }
    return $content;
}

/**
 * 删除字符串首尾配对的字符
 *
 * @param String : $content
 * @param String | Array : $str
 * @return String
 */
function string_double_remove( $content, $str ) {
    if( is_array( $str ) ) {
        foreach( $str as $s ) {
            $content = string_double_remove( $content, $s );
        }
    } else {
        $len = strlen( $str );
        if( substr( $content, 0, $len ) == $str && substr( $content, - $len ) == $str ) {
            $content = substr( $content, $len, - $len );
        }
    }
    return $content;
}

/**
 * 获取配置，优先项目配置
 *
 * @param String : $name
 * @return String
 */
function auto_get_config( $name ) {
    if( ! is_null( Cache :: get( '@.' . $name ) ) ) {
        return Cache :: get( '@.' . $name );
    }
    return Cache :: get( 'OPENSTATION.' . $name );
}

/**
 * 获取文件名中的扩展名部分
 *
 * @param String : $filename
 * @return String
 */
function get_file_ext( $filename ) {
    if( strpos( $filename, '.' ) === FALSE ) {
        return '';
    }
    $filename = trim( basename( $filename ) );
    $temp = explode( '.', $filename );
    return array_pop( $temp );
}

/**
 * 获取文件名中的文件名部分
 *
 * @param String : $filename
 * @return String
 */
function get_file_name( $filename ) {
    $filename = trim( basename( $filename ) );
    if( strpos( $filename, '.' ) === FALSE ) {
        return $filename;
    }
    $temp = explode( '.', $filename );
    return array_shift( $temp );
}

/**
 * 根据变量类型生成唯一字符串
 *
 * @param * : $mix,
 * @return String
 */
function unique_string( $mix ) {

    if( is_object( $mix ) && function_exists( 'spl_object_hash' ) ) {
        return spl_object_hash( $mix );
    } else if( is_resource( $mix ) ) {
        return get_resource_type( $mix ) . strval( $mix );
    } else {
        $mix = serialize( $mix );
    }
    return md5( $mix );
}

/**
 * 获取对象实例
 *
 * @param String : $class, 类名称
 * @param String : $method, 方法名称
 * @param Array : $args, 参数数组
 * @return Object
 */
function get_instance_of( $class, $method = '', $args = array() ) {
    static $_instance = array();
    $identify = empty( $args ) ? $class . $method : $class . $method . unique_string( $args );
    if( ! isset( $_instance[ $identify ] ) ) {
        if( class_exists( $class, NULL ) ) {
            $obj = new $class();
            if( method_exists( $obj, $method ) ) {
                if( ! empty( $args ) ) {
                    $_instance[ $identify ] = call_user_func_array( array( & $obj, $method ), $args );
                } else {
                    $_instance[ $identify ] = $obj -> $method();
                }
            } else {
                $_instance[ $identify ] = $obj;
            }
        } else {
            halt( 'Undefined Class :' . $class, TRUE );
        }
    }
    return $_instance[ $identify ];
}


/**
 * 获取调试信息
 *
 * @param $
 * @return Array
 */
function get_debug_info() {
    return array(
        'include' => import(),
        'runtime' => runtime_count( 'OPENSTATION', TRUE, 3 ),
        'memory_used' => memory_count( 'OPENSTATION', TRUE ),
        'memory_peak' => memory_count( 'OPENSTATION' ),
    );
}

/**
 * 统计内存使用情况
 *
 * @param String : $name, 名称
 * @param Boolean : $finish, 结束并计算
 * @return Array
 */
function memory_count( $name, $finish = FALSE ) {
    static $memory = array();
    if( ! MEMORY_COUNT_ON ) {
        return 0;
    }
    if( $finish && isset( $memory[ $name ] ) ) {
        return abs( $memory[ $name ] - array_sum( explode( ' ', memory_get_usage() ) ) );
    }

    return $memory[ $name ] = array_sum( explode( ' ', memory_get_usage() ) );
}

/**
 * 统计运行时间
 *
 * @param String : $name, 名称
 * @param Boolean : $finish, 结束并计算时间
 * @param Integer : $format
 * @return Array
 */
function runtime_count( $name, $finish = FALSE, $format = 3 ) {
    static $runtime = array();
    if( $finish && isset( $runtime[ $name ] ) ) {
        $count = number_format( microtime( TRUE ) - $runtime[ $name ], $format );
        unset( $runtime[ $name ] );
        return $count;
    } else {
        return $runtime[ $name ] = microtime( TRUE );
    }
    return 0;
}

/**
 * 递归搜索文件，返回文件路径
 * 不要搜索根目录,影响效率
 *
 * @param String : $dir, 目录路径
 * @param String : $name, 文件名
 * @return String
 */
function search_file( $dir, $name ) {
    if( IS_WIN ) {
        $dir = str_replace( '/', DS, $dir );
    } else {
        $dir = str_replace( '\\', DS, $dir );
    }
    $file = string_append_replace( $dir, DS ) . $name;
    if( is_file( $file ) ) {
        return $file;
    }
    // 文件不在根目录，尝试搜索子目录
    foreach( glob( string_append_replace( $dir, DS ) . '*' ) as $path ) {
        if( ! is_dir( $path ) ) {
            continue;
        }
        // 递归搜索子目录
        $result = search_file( $path, $name );
        if( ! is_null( $result ) ) {
            return $result;
        }
    }
    return NULL;
}

/**
 * 代码压缩，去除代码中的空白和注释
 *
 * @param String : $content
 * @return String
 */
function code_compressed( $content ) {
    $result = '';
    $tokens = token_get_all( $content );
    $last_space = FALSE;
    for( $i = 0, $j = count ( $tokens ); $i < $j; $i ++ ) {
        if( is_string( $tokens[ $i ] ) ) {
            $last_space = FALSE;
            $result .= $tokens[ $i ];
        } else {
            switch( $tokens[ $i ][ 0 ] ) {
                case T_OPEN_TAG :
                case T_CLOSE_TAG :
                case T_COMMENT :
                case T_DOC_COMMENT : break;
                case T_WHITESPACE :
                    if ( ! $last_space ) {
                        $result .= ' ';
                        $last_space = TRUE;
                    }
                    break;
                default :
                    $last_space = FALSE;
                    $result .= $tokens[ $i ][ 1 ];
            }
        }
    }
    return $result;
}

/**
 * 编译核心文件缓存
 *
 * @param String : $filename, 缓存文件名
 * @param Boolean : $compressed, 压缩代码
 * @return Boolean
 */
function build_core_cache( $filename, $compressed = FALSE ) {
    if( IS_CLI ) {
        $list = include OS_PATH . 'Common/cores_cli.php';
    } else {
        $list = include OS_PATH . 'Common/cores.php';
    }
    $result = '';
    foreach( $list as $f ) {
        $content = file_get_contents( OS_PATH . $f );
        if( $compressed ) {
            $content = preg_replace('/#<NO-CACHE>(.*?)#<\/NO-CACHE>/s', '', $content );
            $content = code_compressed( $content );
        }
        $result .= $content;
    }
    $result = '<?PHP if( ! defined( \'OS_PATH\' ) ) exit; ' . $result . ' ?>';
    ! is_dir( APP_PATH . 'Data/Build' ) && mkdir( APP_PATH . 'Data/Build' );
    return file_put_contents( $filename, $result );
}

/**
 * 命名风格转换
 *
 * @param String : $name, 缓存名称
 * @param Boolean : $type, TRUE : 转换为驼峰, FALSE : 转换为小写下划线
 * @return String
 */
function named_style( $name, $type = FALSE ) {
    if( $type ) {
        return preg_replace( '/_([a-zA-Z])/e', "strtoupper( '\\1' )", $name );
    } else {
        $name = preg_replace( '/[A-Z]/', "_\\0", $name );
        return strtolower( trim( $name, '_' ) );
    }
}

function md5_encode( $string, $secret ) {
    $result = '';
    if( $string == '' ) return $result;
    $mac = substr( md5( $string ), 8, 10 );
    $key = md5( $mac . $secret );
    $length = strlen( $key );
    for( $i = 0; $i < strlen( strval( $string ) ); $i ++ ) {
        $k = $i % $length;
        $result .= $string[ $i ] ^ $key[ $k ];
    }
    $result = base64_encode( $result ) . $mac;
    return $result;
}

function md5_decode( $string, $secret ) {
    $result = '';
    if( $string == '' ) return $result;
    $mac = substr( $string, -10 );
    $string = base64_decode( substr( $string, 0, strlen( $string ) - 10 ) );
    $key = md5( $mac . $secret );
    $length = strlen( $key );
    for( $i = 0; $i < strlen( $string ); $i ++ ) {
        $k = $i % $length;
        $result .= $string[ $i ] ^ $key[ $k ];
    }
    $result = substr( md5( $result ), 8, 10 ) == $mac ? $result : NULL;
    return $result;
}
?>